#ifndef CVECTOR3_H
#define CVECTOR3_H
// Copyright (C) 2003 IFOnlyRT Project.

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License 2.0 for more details.

// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/

// Official SVN repository and contact information can be found at
// http://code.google.com/p/ifonlyrt/

/*
 * CVector3.hpp
 *
 *  Created on: 15 oct. 2009
 *      Author: Maxime Gaudin
 */

#include <iostream>
#include <string>

#ifndef UINT
	typedef unsigned int uint;
	#define UINT
#endif

/**
 * Classe empêchant de templater n'importe quel type pour un vecteur
 */
template <class T>
class NumbersOnly
{
	private:
		void ValidateType(const int&				i ) const{ }
		void ValidateType(const uint&				i ) const{ }
		void ValidateType(const char&				i) const{ }
		void ValidateType(const unsigned char&		i) const{ }

		void ValidateType(const double&				i) const{ }
		void ValidateType(const long&				i) const{ }
		void ValidateType(const float&				i) const{ }

	public:
		NumbersOnly()
	    {
	       T valid;
	       ValidateType(valid);
	    };
};

/**
 * Classe CVector3 : Gère les vecteurs à 3 dimensions dans un repère MAIN DROITE.
 * @author Maxime Gaudin
 */
template<typename T = double>
class CVector3 {
	public:
		/**
		 * Vecteur nulle
		 * : Vecteur dont toutes composantes sont à Zero.
		 */
		static const CVector3<T> Zero;

		/**
		 * Vecteur unitaire
		 * : Vecteur dont toutes les composantes sont à 1.
		 */
		static const CVector3<T> One;

		/**
		 * Vecteur Up
		 * : Vecteur réprésentant le haut.
		 */
		static const CVector3<T> Up;

		/**
		 * Vecteur Down
		 * : Vecteur représentant le bas.
		 */
		static const CVector3<T> Down;

		/**
		 * Vecteur Right
		 * : Vecteur représentant la droite.
		 */
		static const CVector3<T> Right;

		/**
		 * Vecteur Left
		 * : Vecteur représentant la gauche.
		 */
		static const CVector3<T> Left;

		/**
		 * Vecteur Backward
		 * : Vecteur représentant derrière.
		 */
		static const CVector3<T> Backward;

		/**
		 * Vecteur Forward
		 * : Vecteur représentant la devant.
		 */
		static const CVector3<T> Forward;

	public:
		/**
		 * Constructeur de CVector3 non initialisé
		 * X = Y = Z = 0.
		 */
		CVector3();

		/**
		 * Constructeur de copie de CVector3
		 */
		CVector3(const CVector3<T>& Copy);

		/**
		 * Constructeur de CVector3 initialisé
		 * @param vX = Valeur X du vecteur.
		 * @param vY = Valeur Y du vecteur.
		 * @param vZ = Valeur Z du vecteur.
		 */
		CVector3(const T& vX, const T& vY, const T& vZ);

		/**
		 * Constructeur de CVector3 initialisé
		 * @param Value : X = Y = Z = Value.
		 */
		CVector3(const T& Value);

	public:
		/**
		 * Renvoi la norme du vecteur
		 * : La norme est calculé comme la racine carré de la somme des carrés de ses composantes.
		 */
		T				GetModulus() const;

		/**
		 * Renvoi la norme au carré
		 * : La norme au carré est calculé comme la somme des carrés de ses composantes.
		 * Calculer le carré de la norme est plus économique en terme de calculs.
		 */
		T 				GetSquaredModulus() const;

		/**
		 * Normalise le vecteur
		 * : Divise chaque coordonnée par la norme du vecteur.
		 */
		void 			Normalize();

		/**
		 * Renvoi le vecteur normalisé.
		 */
		CVector3<T>	GetNormalized() const;

	public:
		/**
		 * Fonction statique Dot
		 * : C'est le produit scalaire usuel.
		 * @return Renvoi le produit scalaire de V1 et V2, c'est à dire la somme du produit des composantes de V1 et V2.
		 */
		static T Dot(const CVector3<T>& V1, const CVector3<T>& V2)
		{ return V1.X * V2.X + V1.Y * V2.Y + V1.Z * V2.Z; }
		/**
		 * Fonction statique Cross
		 * : C'est le produit vectoriel usuel.
		 * @return Renvoi le produit vectoriel (Cf http://fr.wikipedia.org/wiki/Produit_vectoriel)
		 */
		static CVector3<T> Cross(const CVector3<T>& V1, const CVector3<T>& V2)
		{
			return CVector3<T>(	V1.Y * V2.Z - V1.Z * V2.Y
								, V1.Z * V2.X - V1.X * V2.Z
								, V1.X * V2.Y - V1.Y * V2.X);
		}

	public:
		/**
		 * Opérateur =
		 * : Opérateur d'affectation
		 */
		CVector3<T> operator =(const CVector3<T>& V2);

		/**
		 * Opérateur []
		 * : [0] = X, [1] = Y et [2] = Z
		 */
		T operator [](const uint i);

		/**
		 * Opérateur +
		 * : Ajoute membre à membre deux CVector3
		 */
		CVector3<T> operator +(const CVector3<T>& V2);

		/**
		 * Opérateur +=
		 * : Ajoute membre à membre deux CVector3
		 */
		CVector3<T> operator +=(const CVector3<T>& V2);

		/**
		 * Opérateur -
		 * : Soustrait membre à membre deux CVector3
		 */
		CVector3<T> operator -(const CVector3<T>& V2);

		/**
		 * Opérateur -=
		 * : Soustrait membre à membre deux CVector3
		 */
		CVector3<T> operator -=(const CVector3<T>& V2);

		/**
		 * Opérateur *
		 * : Multiplie membre à membre un CVector3 par un scalaire
		 */
		CVector3<T> operator *(const T Scalar);

		/**
		 * Opérateur *=
		 * : Multiplie membre à membre un CVector3 par un scalaire
		 */
		CVector3<T> operator *=(const T Scalar);

		/**
		 * Opérateur /
		 * : Divise membre à membre un CVector3 par un scalaire
		 */
		CVector3<T> operator /(const T Scalar);

		/**
		 * Opérateur /=
		 * : Divise membre à membre un CVector3 par un scalaire
		 */
		CVector3<T> operator /=(const T Scalar);

	public:
		/**
		 * Attribut X
		 * : Composante X du vecteur.
		 */
		T X;

		/**
		 * Attribut Y
		 * : Composante Y du vecteur.
		 */
		T Y;

		/**
		 * Attribut Z
		 * : Composante Z du vecteur.
		 */
		T Z;
};

/**
 * Opérateur ==
 * : Permet de comparer deux CVector3
 */
template<typename T, typename U>
bool operator ==(const CVector3<T>& V1, const CVector3<U>& V2)
{
	if(V1.X != V2.X) return false;
	if(V1.Y != V2.Y) return false;
	if(V1.Z != V2.Z) return false;

	return true;
}

/**
 * Opérateur !=
 * : Permet de comparer deux CVector3
 */
template<typename T, typename U>
bool operator !=(const CVector3<T>& V1, const CVector3<U>& V2)
{
	return !(V1 == V2);
}

/**
 * Opérateur <<
 * : Permet d'injecter le vecteur dans un flux.
 */
template<typename T>
std::ostream& operator<<(std::ostream& outStream,const CVector3<T>& V1)
{
	outStream << std::string("{X = ");
	outStream << V1.X << ", Y = ";
	outStream << V1.Y << ", Z = ";
	outStream << V1.Z << ", Length = ";
	outStream << V1.GetModulus() << "}";
	return outStream;
}
#endif /* CVECTOR3_H */

